/*
	http://tlan-pc328:8085/index.php?title=Ajax
*/


function Ajax(id){
	this.constructor.hash[(id || this.constructor.length)] = this;
	this.constructor.length++;
	this.id = id || this.constructor.length;
	var o = this;
	var r = this.constructor.xmlhttp();
	r.onreadystatechange = function(){
		o.RUN.call(o, r);
	};
	this.request = r;
}

Ajax.prototype = {
	toString:function(){return "Ajax ["+this.id+"]";},
	returnType:"text/plain",
	constructor:Ajax,
	type:"REQUEST",
	method:"POST",
	readyState:0,
	postBody:"",
	response:{},
	request:{},
	async:true,
	url:"",

	event:function(name){
		if(this.hasOwnProperty(name)){
			if(arguments.length>1) Array.prototype.shift.call(arguments);
			return this[name].apply(this,arguments);
		}
	},
	
	abort:function(fromServer){
		var r;
		if(this.onAbort)	r = this.event("onAbort",fromServer);
		if(r!=false)			this.request.abort();
	},
	
	RUN:function(http){
		var e=this.constructor.Events, n="";
		
		if(this.readyState == http.readyState) return;
		
		//De 0 a 4 (http://www.quirksmode.org/blog/archives/2005/09/xmlhttp_notes_r_2.html)
		if(this.hasOwnProperty( e[http.readyState] ))
			this.event(e[http.readyState]);
		
		this.readyState = http.readyState;
		
		if( http.readyState == 4 ) {
		
			//Evento numero on505, on408
			if(this[ "on"+http.status ])
			{
				this.event("on"+http.status,this.response,http.status);
			}
				
			if(	e.hasOwnProperty(http.status) )	//Evento basicos onSucess/onTimeout
			{
				e[http.status].call(this,this.response,http.status,true);
			}
			else if(this.onStatus) //Evento generico de onStatus
			{ 
				this.event("onStatus",this.response,http.status);
			}
		
		
			/*
			try {
				if( r.status == undefined || r.status == 0){
					return; //this.onAbort(); //CATCH BY abort function
				}
			}
			catch(e){
				// 13030 is the custom code to indicate the condition -- in Mozilla/FF --
				// when the o object's status and statusText properties are
				// unavailable, and a query attempt throws an exception.
				return;
			}		
			// TODO: Verify Error cases and possible events
			//else if( r.status != 200 || r.statusText != "OK" )
			//this.onError( r.status, r.statusText, r.responseText); 
			*/
		}
		
	},
	
	//*****************************************************************	TIMEOUT
	timeout:0,
	TIMEOUT_ID:-1,
	setTimeout:function(t, call){
		if(!t) t = this.timeout;
		if(!call) call = this.onTimeout;
		if(call){
			function F(){
				var a = arguments.callee; 
				if(a.run.apply(a.scope,a.args)==false){
					a.scope.abortTimeout()
				}
			}
			F.scope = this;
			F.run = this.onTimeout;
			F.args = [false]; //indica se o timeout e' do server
			return (this.TIMEOUT_ID = window.setTimeout(F, t*1000));
		}
		return false;
	},
	abortTimeout:function(){
		this.abort();
		this.clearTimeout();
	},
	clearTimeout:function(){
		window.clearTimeout(this.TIMEOUT_ID);
	},
	
	//*****************************************************************	PARAMETROS
	addParam:function(p,v){		return this.params.add(p,v)},
	removeParam:function(p,v){	return this.params.remove(p,v);},
	getParam:function(p){		return this.params.hash[p];},
	params:{
		hash:{},
		add:function(p,v){
			if(typeof(p) == "object"){
				this.hash = p;
			}else{
				this.hash[p] = v;
			}
		},
		remove:function(p){
			if(!p){ 
				this.hash = {};
			}else{
				delete this.hash[p];
			}
		},
		toString:function(){
			return new String().toQueryString(this.hash);
		}
	},
	
	//*****************************************************************	HEADERS
	addHeader:function(p,v){	return this.header.add(p,v)},
	removeHeader:function(p,v){	return this.header.remove(p,v);},
	getHeader:function(p){		return this.header.hash[p];},
	header:{
		defaultHash:{	"Content-Type":"text/xml",
						"encoding":"ISO-8859-1"},
		hash:{			"Content-Type":"text/xml",
						"encoding":"ISO-8859-1"},
		add:function(p,v){
			if(!p || !v) return false;
			this.hash[p] = v;
		},
		remove:function(p){
			if(!p){
				this.hash = {}
			}else{
				delete this.hash[p];
			}
		},
		reset:function(){
			for(i in this.defaultHash){
				this.hash[i] = this.defaultHash[i];
			}
		},
		resolve:function(o){
			if(o.method.toUpperCase() == "POST"){
				this.add("Content-Type","application/x-www-form-urlencoded");
				this.add("Content-length", o.params.toString().lkength);
				this.add("Connection", "close");
			}
			for(var i in this.hash){
				o.request.setRequestHeader(i, this.hash[i]);
			}
		}
	},

	process:function(url,handler,timeout){
		var u = url || this.url || window.location.href, b=null;
		
		switch(this.method.toUpperCase()){
			case "GET":	if(this.params.toString().length) u += (u.indexOf("?")==-1 ? '?' : '&') + this.params.toString();
			case "HEAD":
			case "POST": b = this.postBody || this.params.toString();
		}
		
		if(this.timeout) this.setTimeout(timeout);
		
		if(	(handler && typeof(handler)=="function" && handler.call(this,u)==false) || 
			this.event("onProcess")==false	)
				return false;
		
		this.request.open(this.method, u, this.async);
		this.header.resolve(this);
		this.request.send(b);
	}
	
}










Ajax.hash = {};
Ajax.length = 0;
Ajax.Events = {
	0:"onUnInitialized",
	1:"onLoading",
	2:"onLoaded",
	3:"onInteractive",
	4:"onComplete",
	
	//SUCCESS;
	200:function(){ 
		try{
			this.response = this.constructor.MimeTypeFilter.render(this.returnType,this);
		}catch(e){
			this.event("onError");
			return false;
		}
		this.event("onSuccess",this.response);
	},	
	//TIMEOUT http://www.checkupdown.com/status/E408.html
	408:function(){ 
		var r;
		r = this.event("onTimeout",this.response,true);
		if(r!=false) this.clearTimeout();
	},		
	//TIMEOUT http://www.checkupdown.com/status/E504.html
	504:function(){ 
		var r;
		r = this.event("onTimeout",this.response,true);
		if(r!=false) this.clearTimeout();
	},
	//ABORT
	552:function(){
		this.abort(true);
	}
}

Ajax.MimeTypeFilter = {
	"text/plain":		function(c){		return c.responseText;	},
	"application/json":	function(c,scope){	return (function(){return eval("("+c.responseText+")")}).call(scope||this);	},
	"text/javascript":	function(c){		return eval(c.responseText);	},
	"text/xml":			function(c){		return c.responseXML;	},
	"text/html":		function(c){		var b = document.createElement("div");b.innerHTML = c.responseText;return b.firstChild;	}
	render:function(mimeType,scope){
		return this[mimeType].call(scope,scope.request);
	}
}


Ajax.PROVIDERS = ["MSXML2.serverXMLHTTP","MSXML2.XMLHTTP.5.0","MSXML2.XMLHTTP.4.0","MSXML2.XMLHTTP.3.0","MSXML2.XMLHTTP","Microsoft.XMLHTTP"];

Ajax.xmlhttp = function(){
	var r;
	if(window.XMLHttpRequest){
		r = new XMLHttpRequest();
	}else if(window.ActiveXObject){
		var i=0;
		while(!r && i<this.PROVIDERS.length){
			try{r=new ActiveXObject(this.ACTIVEX_PROVIDERS[(i++)]);}catch(e){}
		}
	}
	return r;
}
























/* ************************************************************************ Request */
Ajax.Request = function(id){
	this.constructor(id);
}
Ajax.Request.prototype = new Ajax();
Ajax.Request.prototype.constructor = Ajax;


/* ************************************************************************ Bind */
Ajax.Bind = function(id){
	this.constructor(id);
}
Ajax.Bind.prototype = new Ajax();
Ajax.Bind.prototype.constructor = Ajax;
Ajax.Bind.prototype.type = "BIND";


/* ************************************************************************ Updater */
Ajax.Updater = function(id, time){
	this.constructor(id);
	this.time = time || this.time;
	if(this.onLoop){
		function f(){
			var o = arguments.callee;
			o.run.apply(o.scope,o.args);
		}
		f.scope = this;
		f.run = this.process;
		f.args = [];
		this.IntervalID = window.setInterval(f,this.time);
	}
}
Ajax.Updater.prototype = new Ajax();
Ajax.Updater.prototype.constructor = Ajax;
Ajax.Updater.prototype.onLoop = false;
Ajax.Updater.prototype.type = "UPDATER";
Ajax.Updater.prototype.time = 1000;
			


			
String.prototype.toQueryString = function(o){
	var v="",x="";
	for(var i in o){
		v += x + i + "=" + o[i]; x="&";
	};
	return v;
}	